package net.guerlab.sdk.anubis.client.impl;

import com.fasterxml.jackson.databind.ObjectMapper;

import net.guerlab.sdk.anubis.AnubisException;
import net.guerlab.sdk.anubis.Format;
import net.guerlab.sdk.anubis.Method;
import net.guerlab.sdk.anubis.client.AbstractAnubisClient;
import net.guerlab.sdk.anubis.request.AbstractRequest;
import net.guerlab.sdk.anubis.response.AbstractResponse;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * 默认蜂鸟请求客户端
 * 
 * @author guer
 *
 */
public class DefaultAnubisClient extends AbstractAnubisClient {

    /**
     * http请求客户端
     */
    private OkHttpClient client;

    /**
     * objectMapper
     */
    private ObjectMapper objectMapper;

    /**
     * 构造蜂鸟开放平台请求客户端
     * 
     * @param appId
     *            appId
     * @param secretKey
     *            secretKey
     * @param baseUri
     *            基本地址
     * @param client
     *            http请求客户端
     * @param objectMapper
     *            objectMapper
     */
    public DefaultAnubisClient(
            String appId,
            String secretKey,
            String baseUri,
            OkHttpClient client,
            ObjectMapper objectMapper) {
        super(appId, secretKey, baseUri);
        this.client = client;
        this.objectMapper = objectMapper;
    }

    @Override
    public <T extends AbstractResponse<?>> T execute(
            AbstractRequest<T> request) {
        StringBuilder builder = new StringBuilder(baseUri);

        request.setObjectMapper(objectMapper);

        request.createRequestUri(builder, appId, secretKey);

        return execute0(request, builder.toString());
    }

    private <T extends AbstractResponse<?>> T execute0(
            AbstractRequest<T> request,
            String uri) {
        return executeWithHttpRequest(request, uri);
    }

    private <T extends AbstractResponse<?>> T executeWithHttpRequest(
            AbstractRequest<T> request,
            String uri) {
        Builder builder = new Request.Builder();
        builder.url(uri);

        setContent(request, builder);

        return request.parseResponse(getHttpResponceString(builder)).getResponse();
    }

    private <T extends AbstractResponse<?>> void setContent(
            AbstractRequest<T> request,
            Builder builder) {
        String requestBody = request.requestBody();

        if (requestBody == null) {
            return;
        }

        Format format = request.format();
        Method method = request.method();

        MediaType mediaType = MediaType.parse(format.getMediaType());

        RequestBody body = RequestBody.create(mediaType, requestBody);

        switch (method) {
            case POST:
                builder.post(body);
                break;
            case PUT:
                builder.put(body);
                break;
            case DELETE:
                builder.delete(body);
                break;
            default:
        }
    }

    private String getHttpResponceString(
            Builder builder) {
        Call call = client.newCall(builder.build());
        try {
            Response response = call.execute();
            return response.body().string();
        } catch (Exception e) {
            throw new AnubisException(e.getMessage(), e);
        }
    }

    /**
     * 返回 http请求客户端
     *
     * @return http请求客户端
     */
    public OkHttpClient getClient() {
        return client;
    }

    /**
     * 设置http请求客户端
     *
     * @param client
     *            http请求客户端
     */
    public void setClient(
            OkHttpClient client) {
        this.client = client;
    }

    /**
     * 返回 objectMapper
     *
     * @return objectMapper
     */
    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    /**
     * 设置objectMapper
     *
     * @param objectMapper
     *            objectMapper
     */
    public void setObjectMapper(
            ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
}
